﻿using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Graphics;

namespace Mario.Structures
{
    /// <summary>
    /// Controls the collision detection and response behavior of a tile.
    /// </summary>

    enum FaceDirection
    {
        Left = -1,
        Right = 1,
    }
    enum TileCollision
    {
        Passable = 0,
        Impassable = 1,
        Platform = 2,
        Breakable = 3,
        Invisible = 4,
        Special = 5,
        
    }
    enum EdgeCollision
    {
        Passable = 0,
        Impassable = 1,
        Ice = 2,
    }
    enum Special
    {
        Question,
        Mushroom,
        Flower,
        Breaked,
    }
    struct Edges
    {
        public EdgeCollision topEdge;
        public EdgeCollision bottomEdge;
        public EdgeCollision leftEdge;
        public EdgeCollision rightEdge;
    }
    class Tile
    {
        private Animation.Animation idleAnimation;
        private Animation.Animation hitAnimation;
        private SpriteEffects flip = SpriteEffects.None;
        private Animation.AnimationPlayer sprite;

        private SoundEffect breakTile;
        private SoundEffect powerUpAppears;
        private SoundEffect powerUp;
        private SoundEffect questionTile;

        private String textureName;

        public String TextureName
        {
            get { return textureName; }
        }
        public bool IsOnGround
        {
            get { return isOnGround; }
        }
        bool isOnGround;
        private FaceDirection direction = FaceDirection.Right;

        private bool isVisible;
        public bool IsVisible
        {
            get { return isVisible; }
            set { isVisible = value; }
        }
        private float previousBottom;
        public Texture2D texture;
        public TileCollision collision;
        public Special special;
        public Boolean wasHit = false;
        public const int Width = 16;
        public const int Height = 16;
        public static readonly Vector2 Size = new Vector2(Width, Height);
        private Rectangle localBounds;
        private Vector2 position;
        private Edges edges;

        private const float GravityAcceleration = 3500.0f;
        private const float MaxFallSpeed = 600.0f;
        private const float MoveSpeed = 1.0f;

        public Edges Edges
        {
            get { return edges; }
        }
        public Vector2 Position
        {
            get { return position; }
        }
        public Level Level
        {
            get { return level; }
        }
        Level level;
        public Rectangle BoundingRectangle
        {
            get
            {
                int left = (int)Math.Round(position.X - sprite.Origin.X) + localBounds.X;
                int top = (int)Math.Round(position.Y - sprite.Origin.Y) + localBounds.Y;

                return new Rectangle(left, top, localBounds.Width, localBounds.Height);
            }
        }
        public Tile(String textureName, TileCollision collision, Vector2 position, Level level, Boolean isVisible)
        {
            this.level = level;
            this.isVisible = isVisible;
            this.textureName = textureName;
            this.collision = collision;
            this.position = position;
            if (collision == TileCollision.Breakable)
            {

                edges.topEdge = EdgeCollision.Impassable;
                edges.bottomEdge = EdgeCollision.Passable;
                edges.leftEdge = EdgeCollision.Impassable;
                edges.rightEdge = EdgeCollision.Impassable;
            }
            else if (collision == TileCollision.Special)
            {
                edges.topEdge = EdgeCollision.Impassable;
                edges.bottomEdge = EdgeCollision.Passable;
                edges.leftEdge = EdgeCollision.Impassable;
                edges.rightEdge = EdgeCollision.Impassable;
            }
            LoadContent();
        }
        public Tile(String textureName, TileCollision collision, Special special, Vector2 position, Level level, Boolean isVisible)
        {
            this.level = level;
            this.special = special;
            this.isVisible = isVisible;
            this.textureName = textureName;
            this.collision = collision;
            this.position = position;
            if (collision == TileCollision.Breakable)
            {

                edges.topEdge = EdgeCollision.Impassable;
                edges.bottomEdge = EdgeCollision.Passable;
                edges.leftEdge = EdgeCollision.Impassable;
                edges.rightEdge = EdgeCollision.Impassable;
            }
            LoadContent();
        }
        public void LoadContent()
        {

            if (textureName != null)
            {
                String path = "Tiles/" + textureName;
                if (collision == TileCollision.Breakable || collision == TileCollision.Invisible || (collision == TileCollision.Passable && special==Special.Breaked))
                {
                    this.texture = Level.Content.Load<Texture2D>(path);
                    idleAnimation = new Animation.Animation(Level.Content.Load<Texture2D>(path), 0.5f, true);
                    hitAnimation = new Animation.Animation(Level.Content.Load<Texture2D>(path + "Destroyed"), 0.09f, false);
                }
                else if (collision == TileCollision.Special)
                {
                    this.texture = Level.Content.Load<Texture2D>(path);
                    idleAnimation = new Animation.Animation(Level.Content.Load<Texture2D>(path), 0.5f, true);
                    hitAnimation = new Animation.Animation(Level.Content.Load<Texture2D>(path + "Destroyed"), 0.09f, false);
                }
                else
                {
                    this.texture = Level.Content.Load<Texture2D>(path);
                    idleAnimation = new Animation.Animation(Level.Content.Load<Texture2D>(path), 0.5f, true);
                }
                // Calculate bounds within texture size.
                int width = (int)(idleAnimation.FrameWidth);
                int left = (idleAnimation.FrameWidth - width) / 2;
                int height = (int)(idleAnimation.FrameWidth);
                int top = idleAnimation.FrameHeight - height;
                localBounds = new Rectangle(left, top, width, height);
            }
            breakTile = Level.Content.Load<SoundEffect>("Sounds/breakblock");
            powerUp = Level.Content.Load<SoundEffect>("Sounds/powerup");
            powerUpAppears = Level.Content.Load<SoundEffect>("Sounds/powerup_appears");
            questionTile = Level.Content.Load<SoundEffect>("Sounds/coin");
        }
        public void Update(GameTime gameTime)
        {
            float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;
            if (!wasHit)
                if (idleAnimation != null && special!=Special.Breaked)
                    sprite.PlayAnimation(idleAnimation);
                else
                    sprite.PlayAnimation(hitAnimation);
            if (special == Special.Mushroom && this.IsVisible)
            {
                Vector2 velocity = new Vector2((int)direction * MoveSpeed, 0.0f);
                velocity.Y = 3;
                position = position + velocity;
                HandleCollisions();
            }

        }
        private void HandleCollisions()
        {
            // Get the player's bounding rectangle and find neighboring tiles.
            Rectangle bounds = BoundingRectangle;
            int leftTile = (int)Math.Floor((float)bounds.Left / Tile.Width);
            int rightTile = (int)Math.Ceiling(((float)bounds.Right / Tile.Width)) - 1;
            int topTile = (int)Math.Floor((float)bounds.Top / Tile.Height);
            int bottomTile = (int)Math.Ceiling(((float)bounds.Bottom / Tile.Height)) - 1;

            // Reset flag to search for ground collision.
            isOnGround = false;

            // For each potentially colliding tile,
            for (int y = topTile; y <= bottomTile; ++y)
            {
                for (int x = leftTile; x <= rightTile; ++x)
                {
                    // If this tile is collidable,
                    TileCollision collision = Level.GetCollision(x, y);
                    if (collision != TileCollision.Passable)
                    {
                        // Determine collision depth (with direction) and magnitude.
                        Rectangle tileBounds = Level.GetBounds(x, y);
                        Vector2 depth = RectangleExtensions.GetIntersectionDepth(bounds, tileBounds);
                        if (depth != Vector2.Zero)
                        {
                            float absDepthX = Math.Abs(depth.X);
                            float absDepthY = Math.Abs(depth.Y);

                            // Resolve the collision along the shallow axis.
                            if (absDepthY < absDepthX || collision == TileCollision.Platform)
                            {
                                // If we crossed the top of a tile, we are on the ground.
                                if (previousBottom <= tileBounds.Top)
                                    isOnGround = true;

                                // Ignore platforms, unless we are on the ground.
                                if (collision == TileCollision.Impassable || IsOnGround)
                                {
                                    // Resolve the collision along the Y axis.
                                    position = new Vector2(position.X, position.Y + depth.Y);

                                    // Perform further collisions with the new bounds.
                                    bounds = BoundingRectangle;
                                }
                            }
                            else if (collision == TileCollision.Impassable) // Ignore platforms.
                            {
                                // Resolve the collision along the X axis.
                                position = new Vector2(position.X + depth.X, position.Y);

                                // Perform further collisions with the new bounds.
                                bounds = BoundingRectangle;
                            }
                        }
                    }
                }
            }

            // Save the new bounds bottom.
            previousBottom = bounds.Bottom;
        }
        public void OnHit()
        {
            if (hitAnimation != null)
            {
                sprite.PlayAnimation(hitAnimation);
                wasHit = true;
                if (this.collision == TileCollision.Breakable)
                    breakTile.Play();
                if (this.collision == TileCollision.Special)
                    questionTile.Play();
            }
        }
        public void PowerUpCollect()
        {
            powerUp.Play();
        }
        public void PowerUpAppears()
        {
            powerUpAppears.Play();
        }
        public void Draw(GameTime gameTime, SpriteBatch spriteBatch, Vector2 position)
        {
            position.Y += Height;
            position.X += Width / 2;
            sprite.Draw(gameTime, spriteBatch, this.position, flip);
            //spriteBatch.Draw(Texture, position, Color.White);

        }
    }
}
